package br.senai.sc.controller.business;

import java.math.BigDecimal;

import br.senai.sc.controller.exception.BusinessException;

/**
 * Contém os cálculos de IQA para o parâmetro Coliformes.
 * 
 * @author Tiago
 */
public final class CalculoColiformesBusiness {

	/**
	 * Peso fixo utilizado para o cálculo da qualidade dos Coliformes (qi^w)
	 */
	private static final double PESO = 0.15;

	/**
	 * Define o primeiro intervalo para o cálculo dos coliformes fecais. De 2 a
	 * menos que 165.
	 */
	public static final int[] PRIMEIRO_INTERVALO = { 2, 165 };

	/**
	 * Define o segundo intervalo para o cálculo dos coliformes fecais. De 165 a
	 * menos que 7564.
	 */
	public static final int[] SEGUNDO_INTERVALO = { 165, 7564 };

	/**
	 * Define o terceiro intervalo para o cálculo dos coliformes fecais. De 7564
	 * a menos que 96127.
	 */
	public static final int[] TERCEIRO_INTERVALO = { 7564, 96127 };

	/**
	 * Utilizado para a quantidade de casas decimais devem ser utilizadas para o
	 * retorno do calculo de cada intervalo de valor.
	 */
	public static final int ESCALA = 10;

	/**
	 * Utilizado para a quantidade de casas decimais devem ser utilizadas para o
	 * cálculo do valorLN.
	 */
	private static final int ESCALA_VALOR_LN = 20;

	private CalculoColiformesBusiness() {
	}

	/**
	 * Utilizado para fazer o cálculo dos Cloriformes caso o valor informado é
	 * maior ou igual a 2 e menor que 165.
	 * 
	 * Cálculo realizado: -10,4002535714758*LN(B16) + 89,21037447795;
	 * 
	 * @param valor
	 * @return
	 * @throws BusinessException
	 *             Lançada caso o valor não esteja entre o intervalo correto
	 */
	private static BigDecimal primeiroIntervalo(BigDecimal valor)
			throws BusinessException {

		// se o valor for menor que 2, igual ou maior que 165 o mesmo está
		// incorreto
		if ((valor.compareTo(new BigDecimal(PRIMEIRO_INTERVALO[0])) == -1)
				|| (valor.compareTo(new BigDecimal(PRIMEIRO_INTERVALO[1])) >= 0)) {
			throw new BusinessException();
		}

		BigDecimal valorLN = new BigDecimal(Math.log(valor.doubleValue()))
				.setScale(ESCALA_VALOR_LN, IQABusiness.ROUND_MODE);

		// SE(B16<165; -10,4002535714758*LN(B16) + 89,21037447795;
		BigDecimal retorno = new BigDecimal("10.4002535714758")
				.multiply(valorLN).negate()
				.add(new BigDecimal("89.21037447795"));
		return retorno.setScale(ESCALA, IQABusiness.ROUND_MODE);
	}

	/**
	 * Utilizado para fazer o cálculo dos Cloriformes caso o valor informado é
	 * maior ou igual a 165 e menor que 7564.
	 * 
	 * Cálculo realizado: -5,55824236378724*LN(B16) + 59,4084410179226;
	 * 
	 * @param valor
	 * @return
	 * @throws BusinessException
	 *             Lançada caso o valor não esteja entre o intervalo correto
	 */
	private static BigDecimal segundoIntervalo(BigDecimal valor)
			throws BusinessException {

		// se o valor for menor que 165, igual ou maior que 7564 o mesmo está
		// inválido
		if ((valor.compareTo(new BigDecimal(SEGUNDO_INTERVALO[0])) == -1)
				|| (valor.compareTo(new BigDecimal(SEGUNDO_INTERVALO[1])) >= 0)) {
			throw new BusinessException();
		}

		BigDecimal valorLN = new BigDecimal(Math.log(valor.doubleValue()))
				.setScale(ESCALA_VALOR_LN, IQABusiness.ROUND_MODE);

		// SE(B16<7564;-5,55824236378724*LN(B16) + 59,4084410179226;
		BigDecimal retorno = new BigDecimal("5.55824236378724").negate()
				.multiply(valorLN).add(new BigDecimal("59.4084410179226"));
		return retorno.setScale(ESCALA, IQABusiness.ROUND_MODE);
	}

	/**
	 * Utilizado para fazer o cálculo dos Cloriformes caso o valor informado é
	 * maior ou igual a 7564 e menor ou igual a 96127.
	 * 
	 * Cálculo realizado: -2,37511922427945*LN(B16) + 31,0364682671152;
	 * 
	 * @param valor
	 * @return
	 * @throws BusinessException
	 *             Lançada caso o valor não esteja entre o intervalo correto
	 */
	private static BigDecimal terceiroIntervalo(BigDecimal valor)
			throws BusinessException {

		// se o valor for menor que 7564 ou maior que 96127 o mesmo é inválido
		if ((valor.compareTo(new BigDecimal(TERCEIRO_INTERVALO[0])) == -1)
				|| (valor.compareTo(new BigDecimal(TERCEIRO_INTERVALO[1])) > 0)) {
			throw new BusinessException();
		}

		BigDecimal valorLN = new BigDecimal(Math.log(valor.doubleValue()))
				.setScale(ESCALA_VALOR_LN, IQABusiness.ROUND_MODE);

		// SE(B16<=96127;-2,37511922427945*LN(B16) + 31,0364682671152;
		BigDecimal retorno = new BigDecimal("2.37511922427945")
				.multiply(valorLN).negate()
				.add(new BigDecimal("31.0364682671152"));
		return retorno.setScale(ESCALA, IQABusiness.ROUND_MODE);
	}

	/**
	 * Calcula o IQA dos Cloriformessinformado. Cálculo realizado: Se [valor] <
	 * que 0 Lança exceção Se [valor] < 2 IQA = 97 Se [valor] < 165
	 * 
	 * @see {@link CalculoColiformesBusiness#primeiroIntervalo(BigDecimal)} Se
	 *      [valor] < 7564
	 * @see {@link CalculoColiformesBusiness#segundoIntervalo(BigDecimal)} Se
	 *      [valor] <= 96127
	 * @see {@link CalculoColiformesBusiness#terceiroIntervalo(BigDecimal)} Se
	 *      [valor] > 96127 IQA = 2;
	 * 
	 * @param valor
	 * @return
	 * @throws BusinessException
	 *             Lançada caso o valor informado seja inválido (Ex: menor que
	 *             0).
	 */
	public static BigDecimal calculaIQA(BigDecimal valor)
			throws BusinessException {

		// =SE(B16="";"NM";SE(B16<0;"ERR";
		// se o valor informado for menor que zero está incorreto
		if (valor.compareTo(BigDecimal.ZERO) < 0) {
			throw new BusinessException();
		}

		// SE(B16<2; 97;
		if (valor.compareTo(new BigDecimal(PRIMEIRO_INTERVALO[0])) < 0) {
			// Valor retornado caso o valor informado seja menor que o primeiro
			// intervalo
			final int retornoMenorQuePrimeiroIntervalo = 97;
			return new BigDecimal(retornoMenorQuePrimeiroIntervalo);
		}

		// SE(B16<165; -10,4002535714758*LN(B16) + 89,21037447795;
		if (valor.compareTo(new BigDecimal(PRIMEIRO_INTERVALO[1])) < 0) {
			return primeiroIntervalo(valor);
		}

		// SE(B16<7564;-5,55824236378724*LN(B16) + 59,4084410179226;
		if (valor.compareTo(new BigDecimal(SEGUNDO_INTERVALO[1])) < 0) {
			return segundoIntervalo(valor);
		}

		// SE(B16<=96127;-2,37511922427945*LN(B16) + 31,0364682671152;
		if (valor.compareTo(new BigDecimal(TERCEIRO_INTERVALO[1])) <= 0) {
			return terceiroIntervalo(valor);
		}
		// SE(B16>96127; 2)
		// Valor retornado caso o valor informado seja maior que o ultimo
		// intervalo
		final int retornoMaiorQueUltimoIntervalo = 2;
		return new BigDecimal(retornoMaiorQueUltimoIntervalo);
	}

	/**
	 * Calcula a média de qualidade (qi^w) do DBO.
	 * 
	 * @param valor
	 * @return
	 * @throws BusinessException
	 *             Lançada caso o valor informado seja inválido.
	 */
	protected static BigDecimal mediaQualidade(BigDecimal valor)
			throws BusinessException {
		return new BigDecimal(Math.pow(calculaIQA(valor).doubleValue(), PESO));
	}

}
